// 学习下函数调用的深层次原理
// 对于js来说，如果一个函数调用的时候，没有指定作用的对象
// 那么作用域就被指定到了global

// 说白了，函数的调用，本质是决定this指针指向谁的过程

// 像这种定义方式和调用方式，this指针在服务器端会指向global对象
// 在浏览器端会指向window对象
function gua1(arg1, arg2) {
  console.log(this);
}
gua1(); // 这种this在服务端指向了global，在客户端指向了window


function gua2(arg1, arg2) {
  console.log(arg1);
}
gua2(this); // 这种this在服务端指向了{}，在客户端指向了window

(
  function gua3(arg1, arg2) {
    console.log(arg1); // 这种this在服务端指向了{}，在客户端指向了window
  }
)(this, 100);

// 使用()()的方式第一个是规避对全局变量的污染，第二个作用是立即执行
// 比如gua1如果在浏览器中运行就污染了window全局变量

(function (a) {
  console.log(a);  //firebug输出123,使用（）运算符
})(123);

(function (a) {
  console.log(a);  //firebug输出1234，使用（）运算符
}(1234));

!function (a) {
  console.log(a);  //firebug输出12345,使用！运算符
}(12345);

+function (a) {
  console.log(a);  //firebug输出123456,使用+运算符
}(123456);

-function (a) {
  console.log(a);  //firebug输出1234567,使用-运算符
}(1234567);

var fn = function (a) {
  console.log(a);  //firebug输出12345678，使用=运算符
}(12345678);

/**
 * 可以看到输出结果，在function前面加！、+、 -甚至是逗号等到都可以起到函数定义后立即执行的效果，而（）、！、+、-、=等运算符，都将函数声明转换成函数表达式，
 * 消除了javascript引擎识别函数表达式和函数声明的歧义，告诉javascript引擎这是一个函数表达式，不是函数声明，可以在后面加括号，并立即执行函数的代码。
 * 加括号是最安全的做法，因为！、+、-等运算符还会和函数的返回值进行运算，有时造成不必要的麻烦。
 * 
 * 不过这样的写法有什么用呢？
 * javascript中没用私有作用域的概念，如果在多人开发的项目上，你在全局或局部作用域中声明了一些变量，可能会被其他人不小心用同名的变量给覆盖掉，
 * 根据javascript函数作用域链的特性，可以使用这种技术可以模仿一个私有作用域，用匿名函数作为一个“容器”，“容器”内部可以访问外部的变量，
 * 而外部环境不能访问“容器”内部的变量，所以( function(){…} )()内部定义的变量不会和外部的变量发生冲突，俗称“匿名包裹器”或“命名空间”。
 * JQuery使用的就是这种方法，将JQuery代码包裹在( function (window,undefined){…jquery代码…} (window)中，
 * 在全局作用域中调用JQuery代码时，可以达到保护JQuery内部变量的作用。
 * 
 */


var tmp = "hello world";
console.log(tmp.toString().slice(8, -1));


/**
 * 函数的this
 * 在讨论 this 的指向这个问题的时候，本质上是对作用域的讨论。于是乎，首先需要明晰词法作用域和动态作用域这两个概念。
 * let a = 1
 * function foo() {
 *     return a
 * }
 * function bar() {
 *     let a = 2
 *     return foo()
 * }
 * 
 * console.log(foo())
 * console.log(bar())
 */

/**
 * 函数的bind效果
 */

let former = {
  propA: 100,
  funcA: function (input) {
    console.log("in former object, input is %s", input);
    return this.propA;
  }
};

let afterBind = {
  propA : 255
};

console.log("call former object propA is %s", former.funcA('test'));
let ba = former.funcA.bind(afterBind);
console.log("call former object after bind, propA is %s", ba('test2'));

let placeHolder = function(a, _){
  console.log(a,_);
  return _;
}

placeHolder(300,"21");

function UseNewCall(){
  console.log(this);
}

UseNewCall();// 这种执行方法，this指向了global
new UseNewCall();// 这种执行方法，this指向自己